package com.pic.mgt.haishin.executer;

import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.dao.DataAccessException;

import com.pic.mgt.IgxStatus;
import com.pic.mgt.dao.haishin.HaishinBasicBusinessTargetActionInfoBean;
import com.pic.mgt.executer.SimpleConfirmationFileMaker;
import com.pic.mgt.executer.TargetFileProcessException;
import com.pic.mgt.haishin.executer.tar.DeTarFile;
import com.pic.mgt.log.IgxLog;
import com.pic.mgt.manager.terminal.TerminalManager;
import com.pic.mgt.monitor.parser.ConfirmtaionFileForTxFileParser;
import com.pic.mgt.utils.FilePathUtils;


final class ByTerminalTargetFileProcess extends HaishinTargetFileProcess {
	private TerminalManager haishinTerminalManager;
	
	// 此二變數是互斥的，不會同時都不等於 null.
	private String redoAsginTerminal = null;
	private Map<String, Object> lastExecutedTxFileMap = null;
	
	// 和 redoAsginTerminal 配搭使用的
	private Map<String, Object> redoAsginTerminalFileMap;
	
	public ByTerminalTargetFileProcess(DeTarFile deTarFile) {
		super(deTarFile);
	}
	
	@Override
	protected boolean validateTargetDest(String terminal) {
		// 檢查是否為有效的端末
		return 
			this.haishinTerminalManager.isEffectiveTerminalId(terminal);
	}
	
	@Override
	protected void doRedoHaishinBusinessPreprocess()
			throws TargetFileProcessException {
		
		// 從資料庫取得該 redo 的 business 是否有指定要配給的端末
		String redoAsginTerminal = null;
		String redoAsginTerminalFile = null;
		try {
			// DataAccessException
			String[] redoAsginInfo = super.haishinBusinessMgtDao.
											findRedoAsginTerminalAndRedoAsginTerminalFileByUnitName(super.haishinBusiness.getUnitName());
			redoAsginTerminal = redoAsginInfo[0];
			redoAsginTerminalFile = redoAsginInfo[1];
		} catch (DataAccessException e) {
			throw new TargetFileProcessException(
					"Redoing haishin business '" + super.haishinBusiness.getUnitName() +
					"' execution occured a database operation error in preprocess step.", e);
		}
		
		if (null == redoAsginTerminal) {
			/*
			 *  沒有指定要重配的端末,表示此 redo 的 business 要做恢復的動作.
			 *  即是根據業務上次配信的狀態,接著將還沒有完成的配信動作(即解tar)做完. 
			 */
			
			// ********************** Log **********************
			IgxLog.messageLog.info(this,
							"Recovering haishin business '{}' from last execution.",
							super.haishinBusiness.getUnitName());
			// ********************** Log **********************
			
			this.lastExecutedTxFileMap = new HashMap<String, Object>();
			
			// 取出上次已經配送的 target file 清單 (按 creating time 由大到小)
			List<HaishinBasicBusinessTargetActionInfoBean> lastHaishinExecutedTargetActionList;
			try {
				lastHaishinExecutedTargetActionList = 
							super.haishinBusinessTargetActionDao.
								findHaishinBasicBusinessTargetActionInfoListByUnitNameWithOrderByCreatingTimeDESC(
										super.haishinBusiness.getUnitName());
			} catch (DataAccessException e) {
				throw new TargetFileProcessException(
						"Recovering haishin business '" + super.haishinBusiness.getUnitName() + 
						"' occured a database operation error.", e);
			}
			if (null == lastHaishinExecutedTargetActionList || lastHaishinExecutedTargetActionList.isEmpty()) {
				// 表示上回該業務沒有任何已經配成功的交易檔
			}
			else {
				/* 取得第一筆出來(因為第一筆是上回配信業務在進行時中斷掉最後配的交易檔),
				 * 然後去判斷該筆是否上次沒配完,沒配完的話要重配給該筆。要重配的情況為:
				 * (1) 交易檔案存在，且交易檔的"暫存"確認檔也存在
				 * (2) 交易檔案存在，且交易檔的確認檔和確認"暫存"檔都不存在，且沒有上次最後交易時間
				 * 	　 (這種表示上次只做到交易檔產生出來為止而已，"暫存"的確認檔也都還沒產生出來)
				 * 符合2種情況之一，都要重配。
				 */
				HaishinBasicBusinessTargetActionInfoBean latestHaishinBasicBusinessTargetActionInfoBean = 
															lastHaishinExecutedTargetActionList.get(0);
				// 先假設該筆為上次已經執行過，並且是正確的執行(也就是不需要重配)
				this.lastExecutedTxFileMap.put(latestHaishinBasicBusinessTargetActionInfoBean.getFilename(),
											   null);
				if (latestHaishinBasicBusinessTargetActionInfoBean.getResult().
						equals(IgxStatus.HAISHIN_TARGET_ACTION_INEFFECTIVE_TERMINAL)) {
					// 該交易檔是屬於無效的端末,不用再配,加入已執行 Map 清單中  (上面已經加入過)
				}
				else {
					// 得到交易檔案
					File transactionFile = FilePathUtils.multiPathConcat(
														super.haishinOutboxPahtStr,
														latestHaishinBasicBusinessTargetActionInfoBean.getTerminalId(),
														latestHaishinBasicBusinessTargetActionInfoBean.getFilename());
					// 得到交易檔的確認檔
					File confirmationFileForTxFile = 
								ConfirmtaionFileForTxFileParser.getConfirmationFile(transactionFile);
					// 得到交易檔的確認檔的暫存檔 (確認檔暫存檔)
					File confirmationTmpFileForTxFile = SimpleConfirmationFileMaker.getTmepConfirmationFile(confirmationFileForTxFile);
					
					if (transactionFile.exists()) {
						// 表示交易檔存在
						
						if (confirmationTmpFileForTxFile.exists()) {
							// 表示確認暫存檔存在 (表示上回動作未完成，只做到產生暫存檔，必須重配)
							
							this.lastExecutedTxFileMap.remove(latestHaishinBasicBusinessTargetActionInfoBean.getFilename());
						}
						else {
							// 表示確認暫存檔不存在
							
							if (!confirmationFileForTxFile.exists() &&
								null == latestHaishinBasicBusinessTargetActionInfoBean.getLastTransactionTime()) {
								// 表示沒有確認檔也沒有上次最後交易時間 (表示上回動作未完成，只做到產生交易檔而已，必須重配)
								
								this.lastExecutedTxFileMap.remove(latestHaishinBasicBusinessTargetActionInfoBean.getFilename());
							}
						}
					}
				}
				
				/*
				 * 將這些上回已經重配好的交易檔放入已執行 Map 清單中，除了第一筆不要放之外。
				 * 因為第一筆已在上面做另外的判斷和處理.
				 */
				for (int i=1;i<lastHaishinExecutedTargetActionList.size();i++) {
					this.lastExecutedTxFileMap.put(
								lastHaishinExecutedTargetActionList.get(i).getFilename(),
								null);
				}
			}
		}
		else {
			// 表示有指定配給某"一個"端末
			
			// 指定配給的端末
			this.redoAsginTerminal = redoAsginTerminal;
			
			/*
			 *  指定配給端末的哪些檔案 (主要是做系統恢覆時會用到,不是給 user 使用)。
			 *  如果有的話，將它放到 redoAsginTerminalFileMap 裡。
			 */
			if (null != redoAsginTerminalFile) {
				this.redoAsginTerminalFileMap = new HashMap<String, Object>();
				
				String[] redoAsginTerminalFileArray = redoAsginTerminalFile.split(" ");
				for (String asginFile : redoAsginTerminalFileArray) {
					this.redoAsginTerminalFileMap.put(asginFile, null);
				}
			}
			
			// ********************** Log **********************
			IgxLog.messageLog.info(this,
							"Redoing haishin business '{}' execution for terminal '{}'.",
							super.haishinBusiness.getUnitName(), this.redoAsginTerminal);
			// ********************** Log **********************
		}
	}
	
	@Override
	protected boolean isFilteredTxFileInRedoMode(String deTarterminal, String deTarFilename) {
		if (null != this.redoAsginTerminal) {
			/* 表示此 redo 的 business 指定只配給某一個端末.
			 * 只要是符合這個端末和指定配給檔案的交易檔都要配. 
			 */
			
			if (this.redoAsginTerminal.equals(deTarterminal)) {
				if (null != this.redoAsginTerminalFileMap) {
					// 表示有指定要配給的檔案，有在 Map 中的檔案才能配
					return
						!this.redoAsginTerminalFileMap.containsKey(deTarFilename);
				}
				else {
					// 表示沒有指定要配給的檔案，就是只要是該端末的檔案都要配
					return false;
				}
			}
			else {
				// 不符合端末 ID ，不配。
				return true;
			}
		}
		else {
			/*
			 * 表示根據這個業務上次執行的況狀，續繼未完成的配信動作。
			 * lastExecutedTxFileMap 內為上次執行已完成配給的交易檔清單，
			 * 所以只要在這清單內的交易檔都不用再做一次了。
			 */
			
			return this.lastExecutedTxFileMap.containsKey(deTarFilename);
		}
	}
	
	@Override
	protected void deTarFinish() {
		redoAsginTerminal = null;
		redoAsginTerminalFileMap = null;
		lastExecutedTxFileMap = null;
	}
	
	// --- getter and setter ---
	
	public void setHaishinTerminalManager(TerminalManager haishinTerminalManager) {
		this.haishinTerminalManager = haishinTerminalManager;
	}

}
